#ifndef _FONTRENDERER_H_
#define _FONTRENDERER_H_

#include "include_gl.h"
#include "GLProgram.h"
#include "GLVertexArray.h"
#include "GLBuffer.h"
#include "GLTexture.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include <unordered_map>
#include <string>

/**
	Describes one character in a truetype font
	@see FontRenderer
*/
class GlyphDescription
{
public:
	FT_UInt glyphIndex;
	int width, height;
	int bearingX, bearingY;
	int advance;

	int left, top;
};

/**
	Data structure similar to a kd-tree to pack character bitmaps from a truetype font
	@see FontRenderer
*/
class GlyphNode
{
public:
	GlyphNode();
	~GlyphNode();

	GlyphDescription* gd;
	GlyphNode* child[2];
	int left, top, width, height;

	/**
		Packs a new glyph into the tree
	*/
	GlyphNode* insert(GlyphDescription* gdInsert);
};

/**
	Describes a true type font. Contains descriptions for all characters as well as a handle to an OpenGL texture containing all character bitmaps.
	@see FontRenderer
*/
class FaceDescription
{
public:
	~FaceDescription();

	std::vector<GlyphDescription>* glyphs;
	GLTexture* texture;
	std::string fontPath;
	int pixelHeight;
	FT_Face face;
};

struct FontShaderUniformLocations {
	GLint invImageSize;
	GLint pOrigin;
	GLint texture0;
	GLint color;
};

/**
	Encapsulates GL state to draw a string in an orthographic projection.
	@see FontRenderer
*/
class Text
{
	friend class FontRenderer;
public:
	~Text();
	
	/**
		Must be called before calling any other function.
		@param text string to be drawn by the GL
		@param faceDescr true type font that provides the character bitmaps
		@param scene active scene that provides the canvas size
		@param shader shader that is used for the draw call
	*/
	void init(const std::string& text, FaceDescription* faceDescr, GLProgram* shader, FontShaderUniformLocations uniformLocations,  unsigned maxLength = 0);
	/**
		draws the specified string at the specified position
	*/
	void render();
	/**
		sets the pixel position of the string's bottom-left corner in window coordinates
	*/
	void setPosition(int pX, int pY);
	void setText(const std::string& text);
	const std::string& getText() const;
	void setColor(float r, float g, float b);

	float getWidth() const;
	float getHeight() const;
	int getOriginX() const;
	int getOriginY() const;

private:
	/**
	To create a Text object, call the `createText` method in [FontRenderer](@ref FontRenderer)
	*/
	Text();

	std::string _text;
	FaceDescription* _faceDescr;
	GLVertexArray* _vao;
	GLBuffer* _vbo[3];
	GLProgram* _shader;
	FontShaderUniformLocations _uniformLocations;

	int _pX, _pY;
	float _color[3];
	bool _initialized;
	unsigned _maxLength;
	unsigned _length;
	float _width, _height;
};

/**
	Backend for font rendering. Loads true type fonts from a file.
*/
class FontRenderer
{
public:
	FontRenderer();
	~FontRenderer();
	
	/**
		Loads a true type font from `fontPath`. The font is associated with `fontName`. The resulting font can only be used with the specified size and should not be scaled.
		If different font sizes are required, this method should be called once per size.
		Uses the library FreeType to create bitmaps for each character in the set and packs them tightly into one GL texture by using the algorithm outlines on
		[this website](http://www.blackpawn.com/texts/lightmaps/).
		@param fontPath path of the true type font file, relative to the folder fonts/
		@param fontName name that will be associated with this font and size
		@param pixelHeight height of 'W' in pixels
	*/
	void loadFont(const std::string& fontPath, const std::string& fontName, int pixelHeight);

	/**
		Creates a [Text](@ref Text) object with the specified string and font that is associated with `fontName`.
		@param text string to be drawn
		@param fontName name associated with the desired font and size
		@param scene provides canvas size
		@param [out] pointer to creates Text object
	*/
	Text* createText(const std::string& text, const std::string& fontName, unsigned maxLength);

	void setWindowSize(float width, float height);

private:
	FT_Library _ftLibrary;
	std::unordered_map<std::string, FaceDescription> _faces;

	GLProgram* _fontShader;
	FontShaderUniformLocations _fontShaderUniformLocations;
};

#endif